package cz.data.common.mark.mybatis;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.SpringBootVFS;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import cz.data.common.support.db.DbProperty;
import cz.data.common.utils.ThrowableUtil;
import org.apache.ibatis.logging.slf4j.Slf4jImpl;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.apache.ibatis.type.JdbcType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

import javax.sql.DataSource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

public class MybatisFactoryUtils {
    private static final Logger log = LoggerFactory.getLogger(MybatisFactoryUtils.class);
    private static final Map<String, SqlSessionFactory> FACTORY_MAP = new HashMap<>();

    public static SqlSessionFactory sqlSessionFactory(DbProperty property) {
        if (Objects.isNull(property.getId())) {
            return createSqlSessionFactory(property);
        } else {
            SqlSessionFactory sqlSessionFactory = FACTORY_MAP.get(property.getId());
            if (Objects.isNull(sqlSessionFactory)) {
                synchronized (MybatisFactoryUtils.class) {
                    sqlSessionFactory = FACTORY_MAP.get(property.getId());
                    if (Objects.isNull(sqlSessionFactory)) {
                        sqlSessionFactory = createSqlSessionFactory(property);
                        FACTORY_MAP.put(property.getId(), sqlSessionFactory);
                    }
                }
            }
            return sqlSessionFactory;
        }
    }

    public static SqlSessionFactory createSqlSessionFactory(DbProperty property) {
        DataSource dataSource = MybatisUtil.getDataSource(property);
        TransactionFactory transactionFactory = new JdbcTransactionFactory();
        Environment environment = new Environment("development", transactionFactory, dataSource);
        MybatisConfiguration configuration = new MybatisConfiguration(environment);
        configuration.setMapUnderscoreToCamelCase(true);
        configuration.setCacheEnabled(false);
        configuration.addInterceptor(mybatisPlusInterceptor(dbTypeSupport(property.getDbType())));
        configuration.setLogImpl(Slf4jImpl.class);
        configuration.setJdbcTypeForNull(JdbcType.NULL);
        configuration.getMapperRegistry().addMappers("cz.data.domain.masterdata.mybatis.mapper");
        MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        //全局配置
        GlobalConfig globalConfig = new GlobalConfig();
        globalConfig.setBanner(false);
        //配置填充器
        bean.setGlobalConfig(globalConfig);
        bean.setVfs(SpringBootVFS.class);
//        bean.setTypeAliasesPackage("com.cz.di.dynamic.source.api.mybatis.domain");
        bean.setMapperLocations(resolveMapperLocations("classpath:mybatis/*.xml"));
        bean.setConfiguration(configuration);
        return ThrowableUtil.callable(bean::getObject);
    }

    private static String dbTypeSupport(String source) {
        cz.data.common.support.db.DbType dbType = cz.data.common.support.db.DbType.getDbType(source);
        switch (dbType) {
            case POSTGRE_SQL:
                return "postgresql";
            case ORACLE:
                return "oracle";
            case ORACLE_12C:
                return "oracle12c";
            case SQL_SERVER:
            case SQL_SERVER2008:
                return "sqlserver";
            case MARIADB:
                return "mariadb";
            case MYSQL:
            default:
                return "mysql";
        }
    }


    private static MybatisPlusInterceptor mybatisPlusInterceptor(String dbType) {
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.getDbType(dbType));
        // 设置最大单页限制数量，默认 500 条，-1 不受限制
        paginationInnerInterceptor.setMaxLimit(2000L);
        // 分页合理化
        paginationInnerInterceptor.setOverflow(true);
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 分页插件
        interceptor.addInnerInterceptor(paginationInnerInterceptor);
        return interceptor;
    }

    private static Resource[] resolveMapperLocations(String path) {
        ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
        List<String> mapperLocations = new ArrayList<>();
        mapperLocations.add(path);
        List<Resource> resources = new ArrayList<>();
        for (String mapperLocation : mapperLocations) {
            try {
                Resource[] mappers = resourceResolver.getResources(mapperLocation);
                resources.addAll(Arrays.asList(mappers));
            } catch (IOException e) {
                // ignore
            }
        }
        return resources.toArray(new Resource[0]);
    }

}
